#include "LogHelper.h"
#include "common.h"
#include <stdio.h>
#include <unistd.h>
#include <sys/signal.h>
#include <errno.h>
#include <sys/stat.h>
#include <dirent.h>

CLogHelper* CLogHelper::m_pInstance = NULL;
CMutexLock CLogHelper::m_Mutex;

static const char* LogLevelName[NUM_LOG_LEVELS] =
{
  "TRACE ",
  "DEBUG ",
  "INFO  ",
  "WARN  ",
  "ERROR ",
  "FATAL "
};

int CustomFilter(const struct dirent* pDir)
{
    if (0 != strncmp(pDir->d_name, ".", 1) && 0 != strncmp(pDir->d_name, "..", 2))
    {
        return 1;
    }
    return 0;
}

void _WriteLog(const char* filename, int line, int level, const char* mesg, ...)
{
    char _log_[2048] = { 0 }, _stime_[25] = { 0 };

    va_list va;
    struct timeb tp_cur;
    struct tm  _ts_;

    time_t _tt_;
    int i, j;

    ftime(&tp_cur);
    localtime_r(&tp_cur.time, &_ts_);

    snprintf(_stime_, sizeof(_stime_),
        "%04d.%02d.%02d %02d:%02d:%02d.%03d",
        _ts_.tm_year + 1900, _ts_.tm_mon + 1, _ts_.tm_mday, _ts_.tm_hour, _ts_.tm_min, _ts_.tm_sec, tp_cur.millitm
    );

    i = snprintf(_log_, sizeof(_log_),
        "%-20s\t%-d\t%-s\t%-30s\t%-d\t%-s",
        _stime_, CurrentThread::Tid(), LogLevelName[level], filename, line, ""
    );

    va_start(va, mesg);
    j = vsprintf(_log_ + i, mesg, va) + i;
    va_end(va);

    _log_[j] = '\0';
    g_LogQueue.Append(_log_);
}

CLogHelper::CLogHelper(const char* FullDirPath)
{
    m_fp = NULL;
    memset(m_DirPath, 0x00, sizeof(m_DirPath));
    memset(m_FullPathFileName, 0x00, sizeof(m_FullPathFileName));
    
    char absolute_path[MAX_PATH] = { 0 };
    if (FullDirPath == NULL && NULL == realpath("./", absolute_path))
    {
        perror("realpath failed.");
        exit(1);
    }

    if (FullDirPath != NULL)
    {
        char make_dir_path[255] = { 0 };

        memcpy(absolute_path, FullDirPath, strlen(FullDirPath) + 1);
        snprintf(make_dir_path, sizeof(make_dir_path), "mkdir -p %s", absolute_path);
        system(make_dir_path);
    }

    strncpy(m_DirPath, absolute_path, strlen(absolute_path) + 1);
}

CLogHelper::~CLogHelper()
{
    if(m_fp != NULL)
    {
        fclose(m_fp);
        m_fp = NULL;
    }
}

CLogHelper* CLogHelper::GetInstance(const char* FullDirPath)
{
    if(m_pInstance == NULL)
    {
        //double check
        CSynchronizeAccess lock(m_Mutex);
        if(m_pInstance == NULL)
        {
            m_pInstance = new CLogHelper(FullDirPath);
        }
    }
    return m_pInstance;
}

bool CLogHelper::IsFileAlready()
{
    //create a new file
    if (NULL == m_fp || GetFileSize() >= 30 * 1024 * 1204)
    {
        if (m_fp != NULL)
        {
            fclose(m_fp);
            m_fp = NULL;
        }
        Delete_FileCount(20);

        struct tm ts;
        time_t tt;
        time(&tt);
        localtime_r(&tt, &ts);

        snprintf(m_FullPathFileName, sizeof(m_FullPathFileName),
            "%s/Log_%04d%02d%02d%02d%02d%02d.txt",
            m_DirPath, ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec
        );

        int exist = access(m_FullPathFileName, F_OK);

        m_fp = fopen(m_FullPathFileName, "a+");
        if (m_fp != NULL)
        {
            if (exist < 0)
            {
                fprintf(m_fp, "%-s\t\t\t%-s\t%-s\t%-30s\t%-s\t%-s\n", "日期时间", "线程", "级别", "源文件", "行号", "内容");
                fflush(m_fp);
            }
        }
        else
        {
            printf("fopen failed. m_FullPathFileName = %s, error : %s/n", m_FullPathFileName, strerror(errno));
            return false;
        }
    }

    return true;
}

void CLogHelper::Log(Log_Message_T *emt)
{
    if(emt == NULL)
    {
        return;
    }

    AssertFileAlready();

    CSynchronizeAccess lock(m_Mutex);

    struct tm ts;
    char time[20];
    localtime_r(&(emt->occur_time), &ts);

    snprintf(time, sizeof(time), 
        "%04d.%02d.%02d %02d:%02d:%02d", 
        ts.tm_year+1900, ts.tm_mon+1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec
    );

    if(m_fp != NULL)
    {
        fprintf(m_fp, "%-20s\t%-d\t%-s\t%-30s\t%-d\t%-s\n", time, emt->pid, LogLevelName[emt->level], emt->filename, emt->occur_no, emt->error_content);
        fflush(m_fp);
    }
    else
    {
        printf("%s\n", "m_fp is nullptr.");
    }
}

void CLogHelper::Log(const char* msg)
{
    AssertFileAlready();

    CSynchronizeAccess lock(m_Mutex);

    if(m_fp != NULL)
    {
        fprintf(m_fp, "%s\n", msg);
        fflush(m_fp);
    }
    else
    {
        printf("%s\n", "m_fp is nullptr.");
    }
}

void CLogHelper::Delete_File(char* cleandir, int days)
{
    if(cleandir == NULL)
    {
        return;
    }

    CSynchronizeAccess lock(m_Mutex);

    DIR *dir;
    struct dirent *de;
    struct stat buf;
    time_t tim;
    int i;
    dir = opendir(cleandir);
    if(dir==NULL)
    {
        printf("open directory %s error:%s/n",cleandir,strerror(errno));
        return;
    }
    chdir(cleandir);
    while((de=readdir(dir))!=NULL)
    {
        bzero(&buf,sizeof(buf));
        i=lstat(de->d_name,&buf);
        if(i<0)
        {
            printf("i=%d,de->d_name=%s/n",i,de->d_name);
            break;
        }
        else
        {
            if(S_ISDIR(buf.st_mode))
            {
                if((strcmp(de->d_name,".")==0)||(strcmp(de->d_name,".."))==0)
                    continue;
                Delete_File(de->d_name,days);
            }
            else
            {
                time(&tim);
                if (tim - days * 24 * 60 * 60 > buf.st_mtime)
                {
                    printf("delete file:%s/n", de->d_name);
                    unlink(de->d_name);//delete file
                }
            }
        }
    }
    closedir(dir);
}

void CLogHelper::Delete_FileCount(int count)
{
    struct dirent** entry_list, * entry;
    int retCount;
    char pathLog[256] = { 0 };

    retCount = scandir(m_DirPath, &entry_list, CustomFilter, alphasort);
    if (retCount < 0)
    {
        perror("scandir \n");
        return;
    }

    do
    {
        if (retCount <= count)
        {
            break;
        }
        count = retCount - count;

        for (int i = 0; i < count; i++)
        {
            entry = entry_list[i];
            sprintf(pathLog,
                "%s/%s",
                m_DirPath, entry->d_name
            );

            unlink(pathLog);
        }

    } while (0);

    for (int i = 0; i < retCount; ++i)
    {
        free(entry_list[i]);
    }
    free(entry_list);

    return;
}
